#include <gtest/gtest.h>
#include <thread>
#include "scripting/script_interpreter.h"
#include "../sample/battery_level_check.h"
#include "../sample/docking_task.h"
#include "../sample/navigation_task.h"
#include "../sample/screen_return_zero_check.h"
#include "../sample/screen_return_zero_task.h"
#include "../sample/switch_high_current.h"
#include "types/reflex.h"
#include "test_print_info.h"
#include "task/task_chain.h"
#include "task/task_controller.h"
#include "exclude/exclude_policy_factory.h"
#include "exclude_policy/policy_1.h"

using namespace script;

namespace
{
void printErrorInfo(const ErrorInfo &error_info)
{
    printf("%s\n%d:%d, %s\n\n", error_info.line_code.c_str(), error_info.line, error_info.column, error_info.error_message.c_str());
}
}

class TestIfScriptRun: public testing::Test
{
protected:
    std::string scriptPath;
protected:
    void SetUp() override
    {
        scriptPath = "/media/sherlock/d/linux/code/cplusplus/work_ease/tests/script";
        REGISTER_TASK_TYPE(BatteryLevelCheck)
        REGISTER_TASK_TYPE(DockingTask)
        REGISTER_TASK_TYPE(NavigationTask)
        REGISTER_TASK_TYPE(ScreenReturnZeroCheck)
        REGISTER_TASK_TYPE(ScreenReturnZeroTask)
        REGISTER_TASK_TYPE(SwitchHighCurrent)
    }
    void TearDown() override
    {
        UNREGISTER_TASK_TYPE(BatteryLevelCheck)
        UNREGISTER_TASK_TYPE(DockingTask)
        UNREGISTER_TASK_TYPE(NavigationTask)
        UNREGISTER_TASK_TYPE(ScreenReturnZeroCheck)
        UNREGISTER_TASK_TYPE(ScreenReturnZeroTask)
        UNREGISTER_TASK_TYPE(SwitchHighCurrent)
    }
};

TEST_F(TestIfScriptRun, test_run_simple)
{
    const char *code = R"(
        var navigation_task = NavigationTask("navigation_task")
        navigation_task
    )";
    ScriptInterpreter script_run;
    if (auto scheduler = script_run.interpret(code))
    {
        printf("%s\n", scheduler.value()->toString().c_str());
        scheduler.value()->run();
        SUCCEED();
    }
    else
    {
        printErrorInfo(script_run.getErrorInfo());
        FAIL();
    }
}
TEST_F(TestIfScriptRun, test_run_simple_script)
{
    ScriptInterpreter script_run;
    script_run.setWorkPath(scriptPath);
    if (auto scheduler = script_run.interpretSourceFile("test_run_simple_script.bt"))
    {
        printf("%s\n", scheduler.value()->toString().c_str());
        scheduler.value()->run();
        SUCCEED();
    }
    else
    {
        printErrorInfo(script_run.getErrorInfo());
        FAIL();
    }
}
TEST_F(TestIfScriptRun, test_run_simple_task_chain)
{
    task::TaskController task_controller;
    task_controller.setMaxParallelTaskCount(2);

    auto policy_1 = exclude::ExcludePolicyFactory<Policy1>::createExcludePolicy();
//    task_controller.appendPauseExcludePolicy(policy_1);
//    task_controller.appendResumeExcludePolicy(policy_1);
    task_controller.appendDoneExcludePolicy(policy_1);

    const char *code = R"(
        var navigation_task = NavigationTask("navigation_task")
        var docking_task = DockingTask("docking_task")
        var screen_return_zero_task = ScreenReturnZeroTask("screen_return_zero_task")
        var battery_check = BatteryLevelCheck("battery_check")

        #docking_task
        force_pass
        {
            if(battery_check)
            {
                navigation_task
            }
            else
            {
                navigation_task
            }
        }
        #screen_return_zero_task
    )";
    ScriptInterpreter script_run;
    if (auto scheduler = script_run.interpret(code))
    {
        printf("%s\n", scheduler.value()->toString().c_str());
        auto task_chain = new task::TaskChain("123", scheduler.value());

        std::thread([&task_controller]
                    {
                        sleep(1);
                        EASE_INFO("pause...");
                        task_controller.pauseTaskChain("123");
                        sleep(2);
                        task_controller.resumeTaskChain("123");
                    }).detach();

        task_controller.startTaskChain(task_chain);
        while (true)
        {}
        SUCCEED();
    }
    else
    {
        printErrorInfo(script_run.getErrorInfo());
        FAIL();
    }
}
TEST_F(TestIfScriptRun, test_run_simple_if)
{
    const char *code = R"(
        var battery_check = BatteryLevelCheck("battery_check")
        var navigation_task = NavigationTask("navigation_task")
        var docking_task = DockingTask("docking_task")
        var screen_return_zero_check = ScreenReturnZeroCheck("screen_return_zero_check")

        if(battery_check and screen_return_zero_check)
        {
            navigation_task
            docking_task
        }
        else
        {
            docking_task
            navigation_task
        }
    )";
    task::TaskController task_controller;
    task_controller.setMaxParallelTaskCount(2);

    ScriptInterpreter script_run;
    if (auto scheduler = script_run.interpret(code))
    {
        printf("%s\n", scheduler.value()->toString().c_str());
        auto task_chain = new task::TaskChain("123", scheduler.value());
        task_controller.startTaskChain(task_chain);
        std::thread([&task_controller]
                    {
                        sleep(1);
                        EASE_INFO("pause...");
                        task_controller.pauseTaskChain("123");
                        sleep(10);
                        EASE_INFO("resume...");
                        task_controller.resumeTaskChain("123");
                    }).detach();
        while (true)
        {}
        SUCCEED();
    }
    else
    {
        printErrorInfo(script_run.getErrorInfo());
        FAIL();
    }
}

TEST_F(TestIfScriptRun, test_run_2_condition_if)
{
    const char *code = R"(
        var battery_check = BatteryLevelCheck("battery_check")
        var navigation_task = NavigationTask("navigation_task")
        var screen_return_zero_check = ScreenReturnZeroCheck("screen_return_zero_check")

        if(battery_check and screen_return_zero_check)
        {
            navigation_task
        }
    )";
    ScriptInterpreter script_run;
    if (auto scheduler = script_run.interpret(code))
    {
        printf("%s\n", scheduler.value()->toString().c_str());
        scheduler.value()->run();
        SUCCEED();
    }
    else
    {
        printErrorInfo(script_run.getErrorInfo());
        FAIL();
    }
}

TEST_F(TestIfScriptRun, test_run_3_condition_if)
{
    const char *code = R"(
        var battery_check = BatteryLevelCheck("battery_check")
        var navigation_task = NavigationTask("navigation_task")
        var docking_task = DockingTask("docking_task")
        var screen_return_zero_check = ScreenReturnZeroCheck("screen_return_zero_check")
        var switch_high_current = SwitchHighCurrent("switch_high_current")

        if(switch_high_current or !battery_check and screen_return_zero_check)
        {
            navigation_task
        }
        else
        {
            docking_task
        }
    )";
    ScriptInterpreter script_run;
    if (auto scheduler = script_run.interpret(code))
    {
        printf("%s\n", scheduler.value()->toString().c_str());
        scheduler.value()->run();
        SUCCEED();
    }
    else
    {
        printErrorInfo(script_run.getErrorInfo());
        FAIL();
    }
}

TEST_F(TestIfScriptRun, test_run_condition_if_not)
{
    const char *code = R"(
        var battery_check = BatteryLevelCheck("battery_check")
        var navigation_task = NavigationTask("navigation_task")
        var screen_return_zero_check = ScreenReturnZeroCheck("screen_return_zero_check")
        var switch_high_current = SwitchHighCurrent("switch_high_current")

        if(!switch_high_current or !battery_check and screen_return_zero_check)
        {
            navigation_task
        }
    )";
    ScriptInterpreter script_run;
    if (auto scheduler = script_run.interpret(code))
    {
        printf("%s\n", scheduler.value()->toString().c_str());
        scheduler.value()->run();
        SUCCEED();
    }
    else
    {
        printErrorInfo(script_run.getErrorInfo());
        FAIL();
    }
}

TEST_F(TestIfScriptRun, test_run_condition_complex_if_not)
{
    const char *code = R"(
        var battery_check = BatteryLevelCheck("battery_check")
        var screen_return_zero_check = ScreenReturnZeroCheck("screen_return_zero_check")
        var switch_high_current = SwitchHighCurrent("switch_high_current")
        var navigation_task = NavigationTask("navigation_task")

        if(!switch_high_current or battery_check)
        {
            if(screen_return_zero_check)
            {
                navigation_task
            }
        }
    )";
    ScriptInterpreter script_run;
    if (auto scheduler = script_run.interpret(code))
    {
        printf("%s\n", scheduler.value()->toString().c_str());
        scheduler.value()->run();
        SUCCEED();
    }
    else
    {
        printErrorInfo(script_run.getErrorInfo());
        FAIL();
    }
}
